/*
* XCTrack - XContest Live Tracking client for J2ME devices
* Copyright (C) 2009 Petr Chromec <petr@xcontest.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.xcontest.xctrack.gps;
import java.io.DataInputStream;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Random;
import java.util.Vector;
import javax.microedition.io.Connector;
import javax.microedition.io.file.FileConnection;
import javax.microedition.io.file.FileSystemRegistry;
import org.xcontest.xctrack.gps.GpsMessage;
import org.xcontest.xctrack.util.Log;
public class DemoGps extends GpsDriver implements Runnable {
class IGCPoint {
double lon,lat;
int alt;
int t;
}
private Thread _thread;
private double _lat,_lon;
private double _dx,_dy;
private Random _rnd;
private String _address;
private IGCPoint[] _igc;
public DemoGps() {
}
public String getDriverId() {
return "DEMO";
}
/** @return driver name */
public String getName() {
return "Demo GPS";
}
/** Connect the device */
public synchronized void connect(String address) {
_lat = 50;
_lon = 14;
_rnd = new Random();
_dx = 0.0002*(_rnd.nextDouble()-0.5);
_dy = 0.0001*(_rnd.nextDouble()-0.5);
_address = address;
_thread = new Thread(this);
_thread.start();
}
/** Disconnect device */
public synchronized void disconnect() {
_thread.interrupt();
}
private String findIGC() {
Enumeration roots = FileSystemRegistry.listRoots();
Enumeration files;
FileConnection fconn = null;
Vector dirs = new Vector();
while (roots.hasMoreElements()) {
String root = (String)roots.nextElement();
if (root.length() == 0 || root.charAt(root.length()-1) != '/')
root += "/";
dirs.addElement(root);
dirs.addElement(root+"Document/");
dirs.addElement(root+"Other/");
}
for (int i = 0; i < dirs.size(); i ++) {
String dir= (String)dirs.elementAt(i);
Log.info("DemoIGC: searching "+dir);
try {
fconn = (FileConnection)Connector.open("file:///"+dir);
if (fconn.exists()) {
files = fconn.list();
while (files.hasMoreElements()) {
String fn = (String)files.nextElement();
if (fn.toLowerCase().endsWith(".igc")) {
Log.info("DemoIGC: found "+dir+fn);
return "file:///"+dir+fn;
}
}
}
fconn.close();
}
catch (SecurityException e) {
Log.error("DemoIGC: error searching for file",e);
if (fconn != null) {
try {
fconn.close();
}
catch (IOException e1) {}
}
}
catch (IOException e) {
Log.error("DemoIGC: error searching for file",e);
if (fconn != null) {
try {
fconn.close();
}
catch (IOException e1) {}
}
}
}
return null;
}
private int readLine(DataInputStream is, byte[] buf) {
int pos = 0;
int c;
try {
while (true) {
c = is.read();
if (c == -1) return pos == 0 ? -1 : pos; // EOF
if (c == 10 || c == 13) return pos;
if (pos < buf.length) {
buf[pos++] = (byte)c;
}
}
}
catch (IOException e) {
return pos == 0 ? -1 : pos;
}
}
private int getNum(byte[] buf, int start, int len) {
int n = 0;
for (int i = start; i < start+len; i ++)
n = n*10+buf[i]-'0';
return n;
}
private void loadIGC() {
byte[] buf = new byte[1+6+8+9+1+10]; // 'B'+HHMMSS+lat+lon+'A'+alt1,alt2
String fn = findIGC();
int len;
Vector points;
FileConnection fconn = null;
_igc = null;
if (fn != null) {
Log.info("DemoGps: Loading IGC "+fn);
try {
points = new Vector();
fconn = (FileConnection)Connector.open(fn);
DataInputStream is = fconn.openDataInputStream();
while ((len=readLine(is,buf))>=0) {
if (len == buf.length && buf[0] == 'B') {
IGCPoint p = new IGCPoint();
p.t = (getNum(buf,1,2)*3600+getNum(buf,3,2)*60+getNum(buf,5,2))*1000;
p.lat = getNum(buf,7,2)+getNum(buf,9,5)/60000.0;
if (buf[14] == 'S' || buf[14] == 's') p.lat = -p.lat;
p.lon = getNum(buf,15,3)+getNum(buf,18,5)/60000.0;
if (buf[23] == 'W' || buf[23] == 'w') p.lon = -p.lon;
p.alt = getNum(buf,25,5);
if (p.alt == 0) p.alt = getNum(buf,30,5);
points.addElement(p);
}
}
_igc = new IGCPoint[points.size()];
for (int i = 0; i < points.size(); i ++)
_igc[i] = (IGCPoint)points.elementAt(i);
}
catch(IOException e) {
Log.error("DemoGps: Cannot read IGC: "+fn,e);
}
finally {
if (fconn != null) {
try {
fconn.close();
}
catch(IOException e){}
}
}
}
}
public void runIGC() throws InterruptedException {
loadIGC();
int igcPos;
long igcTimeOffset = 0;
long now = System.currentTimeMillis();
boolean isConnected = false;
GpsMessage msg = new GpsMessage();
if (_igc != null && _igc.length > 0) {
igcTimeOffset = now-_igc[0].t;
deviceConnected();
signalReached();
isConnected = true;
}
igcPos = 0;
while(true) {
if (!isConnected) {
Thread.sleep(10000);
}
else if (igcPos >= _igc.length) {
signalLost();
isConnected = false;
}
else {
IGCPoint p = _igc[igcPos];
now = System.currentTimeMillis();
if (now < igcTimeOffset+p.t)
Thread.sleep(igcTimeOffset+p.t-now);
msg.reset();
msg.setPosition(p.lon, p.lat);
msg.setAltitude(p.alt);
msg.setTime(igcTimeOffset+p.t);
notifyListeners(msg);
igcPos ++;
}
}
}
public void runDefault() throws InterruptedException {
deviceConnected();
signalReached();
GpsMessage msg = new GpsMessage();
while (true) {
synchronized(this) {
_lat += _dy;
_lon += _dx;
if (_rnd.nextInt()%40 == 0) {
_dx = 0.0002*(_rnd.nextDouble()-0.5);
_dy = 0.0001*(_rnd.nextDouble()-0.5);
}
else if (_rnd.nextInt()%3 == 0) {
_dx += 0.00002*(_rnd.nextDouble()-0.5);
_dy += 0.00001*(_rnd.nextDouble()-0.5);
}
msg.reset();
msg.setPosition(_lon, _lat);
msg.setAltitude(123);
}
msg.setTime(System.currentTimeMillis());
notifyListeners(msg);
Thread.sleep(_address.equals("ffdemo") ? 5 : 500);
}
}
public void run() {
Log.info("GPS demo driver started");
try {
if (_address.equals("igc"))
runIGC();
else
runDefault();
}
catch (InterruptedException e) {
}
catch (Throwable e) {
Log.error("DemoGps: FATAL",e);
}
deviceDisconnected();
Log.info("GPS demo driver stopped");
}
/** @return the list of visible devices. return value is Vector of GpsDeviceInfo objects */
public GpsDeviceInfo[] scanDevices() {
return new GpsDeviceInfo[] {
new GpsDeviceInfo(this,"Demo GPS Nizbor","demo"),
new GpsDeviceInfo(this,"FAST Demo GPS","ffdemo"),
new GpsDeviceInfo(this,"IGC Replay","igc")
};
}
public boolean hasSingleDevice() {
return false;
}
public boolean isForDebugModeOnly() {
return true;
}
}